﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace WFA
{
    public class Functions
    {

        /// <summary>
        /// Funkcja 10.3 (f1)
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="x2"></param>
        /// <returns></returns>
        public static float F1(float x1, float x2)
        {
            return (float)(Math.Pow((1 + 1 / (x1 * x1) + 1 / Math.Pow(x2, 1.5)), 2));
        }

        /// <summary>
        /// Funkcja 10.4 (f2)
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="x2"></param>
        /// <returns></returns>
        public static float F2(float x1, float x2)
        {
            return (float)(0.1 * x1 + 0.05 * (x1 + x2) * (x1 + x2) + 3 * (1 - x1) * (1 - x1) * Math.Pow(Math.E, (-x1 * x1 - (x2 + 1) * (x2 + 1))) - 10 * (x1 / 5 - x1 * x1 * x1 - x2 * x2 * x2 * x2 * x2) * Math.Pow(Math.E, -x1 * x1 - x2 * x2) - 1 / 3 * Math.Pow(Math.E, -(x1 + 1) * (x1 + 1) - x2 * x2));
        }


        /// <summary>
        /// Funkcja przynależności, zwraca przynależność do odpowiedniego zbioru
        /// </summary>
        /// <param name="a">lewy kraniec dziedziny</param>
        /// <param name="c">prawy kraniec dziedziny</param>
        /// <param name="k">na ile zbiorow podzielic dziedzine [c-a]</param>
        /// <param name="x">obliczyc przynaleznosc do zbioru(ów) dla tej wartości</param>
        /// <returns></returns>
        public static IEnumerable<MembershipStruct> Membership(double a, double c, int k, double x)
        {
            if (x <= a) // lewy kraniec
            {
                yield return new MembershipStruct { A = 0, Value = 1f, Max = a };
            } if (x >= c) // prawy kraniec
            {
                yield return new MembershipStruct { A = k, Value = 1f, Max = c };
            }
            else // najprawdopodobiej znajduje się w dwóch zbiorach rozmytych
            {
                double step = (c - a) / (double)k;

                double p = a + step;

                // wyszukujemy prawy kraniec danego zbioru Ai
                for (int i = 1; i <= k; i++)
                {
                    if (x <= p) // badamy funkcje przynależności
                    {

                        // zbiór i -1
                        MembershipStruct ms = new MembershipStruct();
                        ms.A = i - 1;
                        ms.Max = (float)(p - step);
                        ms.Value = (float)( (p - x) / step);

                        if (ms.Value > 0) yield return ms;

                        // zbiór i
                        ms.A = i;
                        ms.Max = (float)p;
                        ms.Value = (float)((x - p + step) / step);
                        if (ms.Value > 0) yield return ms;

                        yield break;
                    }

                    p = Math.Round(step+p,6);
                }

                //Debugger.Break();
            }

            yield break;
        }

        /// <summary>
        /// Zwraca informację o lewym i prawym krańcu reguły n
        /// </summary>
        /// <param name="n">która to reguła</param>
        /// <param name="a">lewy kraniec dziedziny</param>
        /// <param name="c">prawy kraniec dziedziny</param>
        /// <param name="k">na ile części dziedzina była dzielona</param>
        /// <param name="left">lewy kraniec dziedziny</param>
        /// <param name="right">prawy kraniec dziedziny</param>
        public static void RuleDomain(int n, double a, double c, int k, out double left, out double right)
        {
            double step = (c - a) / k;
            left = a + step * n;
            right = left + step;
        }

        /// <summary>
        /// Zwraca wartość przynależności 'x' do reguły Rn 
        /// </summary>
        /// <param name="n"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double RuleMembership(int n, double x, double a, double c, int k)
        {
            double step = (c - a) / k;

            double left = a + step * n;
            double right = left + step;

            if ((x < left) || (x > right)) return 0;

            if (x < left + step / 2d)
            {
                return (x - left) / (step / 2d);
            }
            else if (x > left + step / 2d)
            {
                return (right - x) / (step / 2d);
            }
            return 1d;
        }
    }

    /// <summary>
    /// Struktura przynależności do zbioru A
    /// </summary>
    public struct MembershipStruct
    {
        /// <summary>
        /// Numer zbioru, np. mamy zbiór A_5, to zróci nam wartość 5
        /// </summary>
        public int A { get; set; }

        /// <summary>
        /// Przynależność do zbioru wartość [0 do 1], chociaż, gdy mamy 0 to struktura nie powinna być zwracana
        /// </summary>
        public double Value { get; set; }

        /// <summary>
        /// Jest to punkt zbioru rozmytego, w którym funkcja przynależności osiąga Max wartość - przydatne przy wyostrzaniu.
        /// </summary>
        public double Max { get; set; }
    }
}
